package com.knife.oauth.app.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.knife.common.exception.CommonException;
import com.knife.oauth.app.service.IamRoleService;
import com.knife.oauth.domain.entity.IamMemberRole;
import com.knife.oauth.domain.entity.IamMenu;
import com.knife.oauth.domain.entity.IamRole;
import com.knife.oauth.domain.entity.IamRoleMenu;
import com.knife.oauth.infra.mapper.IamMemberRoleMapper;
import com.knife.oauth.infra.mapper.IamMenuMapper;
import com.knife.oauth.infra.mapper.IamRoleMapper;
import com.knife.oauth.infra.mapper.IamRoleMenuMapper;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author lth
 * @created 2025年08月15日 星期五 8:59
 */
@Service
public class IamRoleServiceImpl implements IamRoleService {
    @Resource
    private IamRoleMapper iamRoleMapper;
    @Resource
    private IamRoleMenuMapper iamRoleMenuMapper;
    @Resource
    private IamMemberRoleMapper iamMemberRoleMapper;
    @Resource
    private IamMenuMapper iamMenuMapper;

    @Override
    public Page<IamRole> selectRolePage(Page<IamRole> pageRequest, IamRole iamRole) {
        LambdaQueryWrapper<IamRole> iamRoleLambdaQueryWrapper = new LambdaQueryWrapper<>();
        iamRoleLambdaQueryWrapper
                .like(StringUtils.isNotEmpty(iamRole.getCode()), IamRole::getCode, iamRole.getCode())
                .like(StringUtils.isNotEmpty(iamRole.getDescription()), IamRole::getDescription, iamRole.getDescription())
                .eq(IamRole::getTenantId, iamRole.getTenantId())
                .orderByDesc(IamRole::getCreationDate);
        Page<IamRole> iamRolePage = iamRoleMapper.selectPage(pageRequest, iamRoleLambdaQueryWrapper);
        List<Long> roleIdList = iamRolePage.getRecords().stream().map(IamRole::getId).collect(Collectors.toList());
        LambdaQueryWrapper<IamRoleMenu> iamRoleMenuLambdaQueryWrapper = new LambdaQueryWrapper<>();
        iamRoleMenuLambdaQueryWrapper.in(IamRoleMenu::getRoleId,roleIdList).eq(IamRoleMenu::getTenantId, iamRole.getTenantId());
        List<IamRoleMenu> iamRoleMenus = iamRoleMenuMapper.selectList(iamRoleMenuLambdaQueryWrapper);
        Map<Long, List<IamRoleMenu>> roleMenuMap = iamRoleMenus.stream().collect(Collectors.groupingBy(IamRoleMenu::getRoleId));
        List<Long> finalNodeIdList = findFinalIdList();
        iamRolePage.getRecords().forEach(item->{
            List<IamRoleMenu> tempMenuList = roleMenuMap.get(item.getId());
            if (CollectionUtils.isNotEmpty(tempMenuList)){
                // 只关联最末级,因为如果关联了上级,会导致有些不被勾选的勾选到了
                item.setCheckedIdList(tempMenuList.stream().map(IamRoleMenu::getMenuId).filter(finalNodeIdList::contains).collect(Collectors.toList()));
            }
        });
        return iamRolePage;
    }

    /**
     * 找出最末级节点
     * @return 最末级节点
     */
    private List<Long> findFinalIdList(){
        List<IamMenu> iamMenus = iamMenuMapper.selectList(null);
        // 找最后节点
        List<Long> finalNodeIdList = new ArrayList<>();
        for (IamMenu iamMenu : iamMenus) {
            boolean finalFlag = true;
            for (IamMenu iamMenu2 : iamMenus) {
                if (!iamMenu.getId().equals(iamMenu2.getId()) && iamMenu.getId().equals(iamMenu2.getParentId())){
                    finalFlag = false;
                    break;
                }
            }
            if (finalFlag){
                finalNodeIdList.add(iamMenu.getId());
            }
        }
        return finalNodeIdList;
    }

    /**
     * 找出最末级节点
     * @param checkedIdList 传入的是子级数据
     * @return 最末级节点
     */
    private void addParentIdList(List<Long> checkedIdList){
        List<IamMenu> iamMenus = iamMenuMapper.selectList(null);
        List<IamMenu> menuList = iamMenus.stream().filter(item -> checkedIdList.contains(item.getId())).collect(Collectors.toList());
        // 找父节点
        for (IamMenu iamMenu : menuList) {
            recurrence(checkedIdList,iamMenus,iamMenu);
        }
    }

    private void recurrence(List<Long> checkedIdList,List<IamMenu> iamMenus,IamMenu iamMenu){
        if (iamMenu.getParentId() != null && !checkedIdList.contains(iamMenu.getParentId())){
            checkedIdList.add(iamMenu.getParentId());
            List<IamMenu> parentList = iamMenus.stream().filter(item -> item.getId().equals(iamMenu.getParentId())).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(parentList)){
                recurrence(checkedIdList, iamMenus, parentList.get(0));
            }
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public IamRole saveOrUpdateRole(IamRole iamRole) {
        if (Objects.isNull(iamRole.getId())){
            // 新增角色和对应菜单分配
            iamRoleMapper.insert(iamRole);
            List<Long> checkedIdList = iamRole.getCheckedIdList();
            insertIamRoleMenu(checkedIdList,iamRole.getId(),iamRole.getTenantId());
        }else {
            // 修改角色信息及菜单分配
            IamRole db = iamRoleMapper.selectById(iamRole.getId());
            db.setName(iamRole.getName());
            db.setDescription(iamRole.getDescription());
            iamRoleMapper.updateById(db);
            // 修改菜单分配
            List<Long> checkedIdList = iamRole.getCheckedIdList();
            // 补全父级id
            addParentIdList(checkedIdList);
            LambdaQueryWrapper<IamRoleMenu> iamRoleMenuLambdaQueryWrapper = new LambdaQueryWrapper<>();
            iamRoleMenuLambdaQueryWrapper.eq(IamRoleMenu::getRoleId,iamRole.getId()).eq(IamRoleMenu::getTenantId, iamRole.getTenantId());
            List<IamRoleMenu> iamRoleMenus = iamRoleMenuMapper.selectList(iamRoleMenuLambdaQueryWrapper);
            // 以前端传入数据为准,多了的插入,少了的删除
            for (IamRoleMenu iamRoleMenu : iamRoleMenus) {
                if (!checkedIdList.contains(iamRoleMenu.getMenuId())){
                    iamRoleMenuMapper.deleteById(iamRoleMenu);
                }
            }
            List<@NotNull Long> menuIdList = iamRoleMenus.stream().map(IamRoleMenu::getMenuId).collect(Collectors.toList());
            checkedIdList.removeAll(menuIdList);
            insertIamRoleMenu(checkedIdList,iamRole.getId(),iamRole.getTenantId());
        }
        return iamRole;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public IamRole deleteRole(IamRole iamRole) {
        LambdaQueryWrapper<IamMemberRole> iamMemberRoleLambdaQueryWrapper = new LambdaQueryWrapper<>();
        iamMemberRoleLambdaQueryWrapper.eq(IamMemberRole::getRoleId,iamRole.getId()).eq(IamMemberRole::getTenantId,iamRole.getTenantId());
        Long userCount = iamMemberRoleMapper.selectCount(iamMemberRoleLambdaQueryWrapper);
        if (userCount>0){
            throw new CommonException("该角色已关联了用户,请先去除关联的用户");
        }

        iamRoleMapper.deleteById(iamRole);
        LambdaQueryWrapper<IamRoleMenu> iamRoleMenuLambdaQueryWrapper = new LambdaQueryWrapper<>();
        iamRoleMenuLambdaQueryWrapper.eq(IamRoleMenu::getRoleId,iamRole.getId()).eq(IamRoleMenu::getTenantId, iamRole.getTenantId());
        List<IamRoleMenu> iamRoleMenus = iamRoleMenuMapper.selectList(iamRoleMenuLambdaQueryWrapper);
        List<@NotNull Long> menuIdList = iamRoleMenus.stream().map(IamRoleMenu::getMenuId).collect(Collectors.toList());
        iamRoleMenuMapper.deleteBatchIds(menuIdList);
        return iamRole;
    }

    private void insertIamRoleMenu(List<Long> menuIdList,Long roleId,Long tenantId){
        if (CollectionUtils.isNotEmpty(menuIdList)){
            for (Long menuId : menuIdList) {
                IamRoleMenu iamRoleMenu = new IamRoleMenu();
                iamRoleMenu.setRoleId(roleId);
                iamRoleMenu.setMenuId(menuId);
                iamRoleMenu.setTenantId(tenantId);
                iamRoleMenuMapper.insert(iamRoleMenu);
            }
        }
    }
}
